package jcurses.widgets;

import jcurses.event.ValueChangedEvent;
import jcurses.event.ValueChangedListener;
import jcurses.event.ValueChangedListenerManager;
import jcurses.system.CharColor;
import jcurses.system.InputChar;
import jcurses.system.Toolkit;
import jcurses.util.Rectangle;

/**
 * This class implements a checkbox widget. This checkboxes state is modified by
 * typing a special char (default 'space'). You can register listeners by this
 * widget to track state changes.
 */
public class CheckBox extends Widget implements IFocusable {

	private boolean _checked = false;
	private FocusData focus = new FocusData();

	private ValueChangedListenerManager _listenerManager = new ValueChangedListenerManager();

	private static CharColor __checkBoxDefaultColors = new CharColor(
			CharColor.WHITE, CharColor.BLACK);

	private static CharColor __focusedCheckBoxDefaultColors = new CharColor(
			CharColor.BLUE, CharColor.WHITE, CharColor.REVERSE);

	private CharColor _focusedCheckboxColors = getFocusedCheckboxDefaultColors();
	private static InputChar __changeStatusChar = new InputChar(' ');

	/**
	 * The constructor creates an unchecked checkbox
	 */
	public CheckBox() {
		this(false);

	}

	/**
	 * The constructor.
	 * 
	 * @param checked
	 *            true, if the checkbox is checked at first time, false
	 *            otherwise
	 */
	public CheckBox(boolean checked) {
		_checked = checked;
	}

	/**
	 * Adds listener to the checkbox to track states changes
	 * 
	 * @param listener
	 *            listener to add
	 */
	public void addListener(ValueChangedListener listener) {
		_listenerManager.addListener(listener);
	}

	@Override
	public CharColor getDefaultColors() {
		return __checkBoxDefaultColors;
	}

	@Override
	public void getFocus() {
		this.focus.focus = true;
		changeColors();
	}

	/**
	 * @return checkboxes colors, if it is focused
	 */
	public CharColor getFocusedCheckboxColors() {
		return _focusedCheckboxColors;
	}

	/**
	 * return true, if the checkbox is checked , false otherwise
	 */
	public boolean getValue() {
		return _checked;
	}

	@Override
	public boolean hasFocus() {
		return this.focus.focus;
	}

	@Override
	public boolean isFocusable() {
		return this.focus.focusable;
	}

	@Override
	public void lostFocus() {
		this.focus.focus = false;
		changeColors();
	}

	/**
	 * Removes listener from the checkbox
	 * 
	 * @param listener
	 *            to remove
	 */
	public void removeListener(ValueChangedListener listener) {
		_listenerManager.removeListener(listener);
	}

	@Override
	public void setFocus(boolean value) {
		if (value) {
			getFocus();
		} else {
			lostFocus();
		}
	}

	@Override
	public void setFocusable(boolean focusable) {
		this.focus.focusable = focusable;
	}

	/**
	 * Sets colors of the checkbox in focused state.
	 * 
	 * @param colors
	 *            checkboxes colors, if it is focused
	 */
	public void setFocusedCheckboxColors(CharColor colors) {
		_focusedCheckboxColors = colors;
	}

	/**
	 * Sets checkboxes value
	 * 
	 * @param value
	 *            if the checkbox becomes checked , false otherwise
	 */
	public void setValue(boolean value) {
		boolean oldValue = _checked;
		_checked = value;
		if (oldValue != _checked) {
			_listenerManager.handleEvent(new ValueChangedEvent(this));
		}
		paint();
	}

	private void changeColors() {
		CharColor colors = hasFocus() ? getFocusedCheckboxColors()
				: getColors();
		Toolkit.changeColors(getRectangle(), colors);
	}

	private CharColor getFocusedCheckboxDefaultColors() {
		return __focusedCheckBoxDefaultColors;
	}

	@Override
	protected void doPaint() {
		Rectangle rect = (Rectangle) getSize().clone();
		rect.setLocation(getAbsoluteX(), getAbsoluteY());
		String text = "[" + ((_checked) ? "X" : " ") + "]";
		CharColor colors = hasFocus() ? getFocusedCheckboxColors()
				: getColors();
		Toolkit.printString(text, rect, colors);
	}

	@Override
	protected void doRepaint() {
		doPaint();
	}

	@Override
	protected Rectangle getPreferredSize() {
		return new Rectangle(3, 1);
	}

	protected boolean handleInput(InputChar ch) {
		if (ch.equals(__changeStatusChar)) {
			setValue((_checked) ? false : true);
			paint();
			return true;
		}

		return false;
	}

}
